#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<assert.h>
#include<ctype.h>
#include<errno.h>
#define NUM 1024 
#define OPT_NUM 64
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
#define ERROR_REDIR 4
#define trimSpace(start) do{\
  while(isspace(*start)) start++;\
}while(0)

char lineCommand[NUM];
char *myargv[OPT_NUM];//指针数组
int lastCode=0;
int lastSig=0;
int redirType=NONE_REDIR;
char* redirFile=NULL;
void commandCheck(char* commands)
{
  assert(commands);
  char* start=commands;
  char* end=commands+strlen(commands);
  while(start<end)
  {
    if(*start=='>')//输出重定向
    {
          *start='\0';
          start++;
          if(*start=='>')//追加重定向
          {
            redirType=APPEND_REDIR;
            start++;
          }
          else{
            redirType=OUTPUT_REDIR;
          }
          trimSpace(start);
          redirFile=start;
          break;
    }
    else if(*start=='<')//输入重定向
    {
        //"cat < file.txt"
        *start='\0';
        start++;
        trimSpace(start);
        //填写重定向信息
        redirType=INPUT_REDIR;
        redirFile=start;
        break;
    }
    else{
      start++;
    }
  }
}
int main()
{
  while(1)
  {
    redirType=NONE_REDIR;
    redirFile=NULL;
  //输出提示符
  printf("用户名@主机名 当前路径#");
  fflush(stdout);
  //获取用户输入,输入的时候，输入\n(回车)
  char* s=fgets(lineCommand,sizeof(lineCommand)-1,stdin);
  assert(s!=NULL);
  //清楚最后一个\n
  lineCommand[strlen(lineCommand)-1]=0;
 // printf("test:%s\n",lineCommand);
  //sleep(10);
  commandCheck(lineCommand);
//字符串切割
  myargv[0]=strtok(lineCommand," ");
  //如果没有子串，strtok->NULL,myargv[end]=NULL
  int i=1;
  if(myargv[0]!=NULL && strcmp(myargv[0],"ls")==0)
  {
        myargv[i++]=(char*)"--color=auto";
  }
  while(myargv[i++]=strtok(NULL," "));//故意写的等号
 //如果是cd命令，不需要创建子进程，让shell自己执行对应的命令，本质就是执行系统调用--内建/内置命令
  if(myargv[0]!=NULL&&strcmp(myargv[0],"cd")==0)
 {
   if(myargv[1]!=NULL)
     chdir(myargv[1]);
   continue;
 }
  if(myargv[0]!=NULL && myargv[1]!=NULL && strcmp(myargv[0]),"echo")==0)
  {
  if(strcmp(myargv[1],"$?")==0)
  {
    printf("%d,%d\n",lastCode,lastSig);
  }
  else{
    printf("%s\n",argv[1]);
  }
  continue;
  }
  //测试是否成功 条件编译
  //在Makefile中定义宏测试
#ifdef DEBUG
  for(i=0;myargv[i];i++)
  {
     printf("myargv[%d]:%s\n",i,myargv[i]);
  }
#endif
  //执行命令
  pid_t id=fork();
  assert(id!=-1);
  if(id==0)
  {
    //因为命令是子进程执行的，真正的重定向工作一定是子进程来完成
    //如何重定向，是父进程要给子进程提供信息的
    //子进程重定向会影响父进程吗?no,进程具有独立性
    switch(redirType)
    {
      case NONE_REDIR:
        //什么都不做
        break;
      case INPUT_REDIR:
        {
          int fd=open(redirFile,O_RDONLY,0666);
          if(id<0)
          {perror("open");
            exit(errno);
         }//重定向的文件已经打开了
          dup2(fd,0);
        }
        break;
      case OUTPUT_REDIR:
      case APPEND_REDIR:
        {
          int flags=O_WRONLY|O_CREAT;
          if(redirType==APPEND_REDIR) flags|=O_APPEND;
          else flags|O_TRUNC;

          int fd=open(redirFile,flags,0666);
          if(id<0)
          {
            perror("open");
            exit(errno);
          }
          dup(fd,1);

        }
        break;
      default:
        printf("bug?\n");
        break;
    }
    execvp(myargv[0],myargv);//执行程序替换时，不会影响前面的文件重定向
    exit(1);
  }
  int status=0;
  pid_t ret=waitpid(id,&status,0);
  assert(ret>0);
 (void)ret;
  lastCode=((status>>8)&0xFF);
  lastSig=(status&0x7F);
  }
}
